package com.open4cn.simulator.connect.tcp;

import com.open4cn.simulator.core.connector.AbstractConnector;
import com.open4cn.simulator.core.connector.ConnectAddress;
import com.open4cn.simulator.core.connector.ConnectorModeEnum;
import com.open4cn.simulator.core.exception.IllegalParameterException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * @author yinxiaopeng
 * @version 1.0.0
 * @company 致景科技
 * @department 飞梭BU/研发
 * @description
 * @Email yinxiaopeng@zj.tech
 * @date 2022-03-14 23:57
 */
public class TCPServerConnector extends AbstractConnector {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public TCPServerConnector() {
        super(ConnectorModeEnum.TCP_SERVER);
    }

    private ServerBootstrap serverBootstrap;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private EventLoopGroup handlerGroup;
    private ChannelFuture channelFuture;


    // 入站处理器
    private List<ChannelInboundHandler> beforeDecodHandlers = new ArrayList<>();

    private List<Class<? extends ChannelInboundHandler>> decoderHandlers = new ArrayList<>();

    private List<ChannelInboundHandler> afterDecodHandlers = new ArrayList<>();

    // 出站处理器
    private List<ChannelOutboundHandler> beforeEncodHandlers = new ArrayList<>();

    private List<Class<? extends ChannelOutboundHandler>> encoderHandlers = new ArrayList<>();

    private List<ChannelOutboundHandler> afterEncodHandlers = new ArrayList<>();

    // Future回调监听
    private List<GenericFutureListener<ChannelFuture>> futureListeners = new ArrayList();


    public <T extends ChannelInboundHandler> void beforeDecod(T channelInboundHandler) {
        if (!beforeDecodHandlers.contains(channelInboundHandler)) {
            beforeDecodHandlers.add(channelInboundHandler);
        }
    }


    public void decoder(Class<? extends ChannelInboundHandler> channelInboundHandler) {
        if (!decoderHandlers.contains(channelInboundHandler)) {
            decoderHandlers.add(channelInboundHandler);
        }
    }


    public <T extends ChannelInboundHandler> void afterDecod(T channelInboundHandler) {
        if (!afterDecodHandlers.contains(channelInboundHandler)) {
            afterDecodHandlers.add(channelInboundHandler);
        }
    }

    public <E extends ChannelOutboundHandler> void beforeEncod(E channelOutboundHandler) {
        if (!beforeEncodHandlers.contains(channelOutboundHandler)) {
            beforeEncodHandlers.add(0, channelOutboundHandler);
        }
    }

    public void encoder(Class<? extends ChannelOutboundHandler> channelOutboundHandler) {
        if (!encoderHandlers.contains(channelOutboundHandler)) {
            encoderHandlers.add(0, channelOutboundHandler);
        }
    }

    public <E extends ChannelOutboundHandler> void afterEncod(E channelOutboundHandler) {
        if (!afterEncodHandlers.contains(channelOutboundHandler)) {
            afterEncodHandlers.add(0, channelOutboundHandler);
        }
    }


    public void addFutureListenner(GenericFutureListener<ChannelFuture> futureListenerWrapper) {
        if (!futureListeners.contains(futureListenerWrapper)) {
            futureListeners.add(futureListenerWrapper);
        }
    }


    @Override
    public void connect(ConnectAddress address) {
        close();
        if (!address.check(getConnectorMode())) {
            throw new IllegalParameterException(String.format("连接地址非法:%s", address.toString()));
        }
        setConnectAddress(address);
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup(1);
        handlerGroup = new DefaultEventLoopGroup(1);
        serverBootstrap = new ServerBootstrap()
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 绑定入站处理器
                        if (!beforeDecodHandlers.isEmpty()) {
                            beforeDecodHandlers.forEach(channelInboundHandler -> {
                                logger.info("注册handler:{}", channelInboundHandler.getClass().getName());
                                socketChannel.pipeline().addLast(handlerGroup, channelInboundHandler);
                            });
                        }
                        if (!decoderHandlers.isEmpty()) {
                            decoderHandlers.forEach(channelInboundHandler -> {
                                logger.info("注册handler:{}", channelInboundHandler.getName());
                                try {
                                    ChannelInboundHandler handler = channelInboundHandler.newInstance();
                                    socketChannel.pipeline().addLast(handlerGroup, handler);
                                } catch (Exception e) {
                                    logger.error("注册handler异常:{}", channelInboundHandler.getName());
                                }
                            });
                        }
                        if (!afterDecodHandlers.isEmpty()) {
                            afterDecodHandlers.forEach(channelInboundHandler -> {
                                logger.info("注册handler:{}", channelInboundHandler.getClass().getName());
                                socketChannel.pipeline().addLast(handlerGroup, channelInboundHandler);
                            });
                        }

                        // 绑定出站处理器
                        if (!afterEncodHandlers.isEmpty()) {
                            afterEncodHandlers.forEach(channelOutboundHandler -> {
                                logger.info("注册handler:{}", channelOutboundHandler.getClass().getName());
                                socketChannel.pipeline().addLast(handlerGroup, channelOutboundHandler);
                            });
                        }

                        if (!encoderHandlers.isEmpty()) {
                            encoderHandlers.forEach(channelOutboundHandler -> {
                                logger.info("注册handler:{}", channelOutboundHandler.getName());
                                try {
                                    ChannelOutboundHandler handler = channelOutboundHandler.newInstance();
                                    socketChannel.pipeline().addLast(handlerGroup, handler);
                                } catch (Exception e) {
                                    logger.error("注册handler异常:{}", channelOutboundHandler.getName());
                                }
                            });
                        }

                        if (!beforeEncodHandlers.isEmpty()) {
                            beforeEncodHandlers.forEach(channelOutboundHandler -> {
                                logger.info("注册handler:{}", channelOutboundHandler.getClass().getName());
                                socketChannel.pipeline().addLast(handlerGroup, channelOutboundHandler);
                            });
                        }
                    }
                });
        channelFuture = serverBootstrap.bind(address.getPort());
        if (!futureListeners.isEmpty()) {
            futureListeners.forEach(listener -> {
                logger.info("注册listener:{}", listener.getClass().getName());
                channelFuture.addListeners(listener);
            });
        }
    }

    @Override
    public boolean close() {
        if (isClosed()) {
            return true;
        }

        if (null != channelFuture) {
            channelFuture.channel().close();
        }
        if (null != handlerGroup) {
            handlerGroup.shutdownGracefully();
        }
        if (null != workerGroup) {
            workerGroup.shutdownGracefully();
        }
        if (null != bossGroup) {
            bossGroup.shutdownGracefully();
        }
        return beClosed();
    }

    @Override
    public void destroy() {
        if (isClosed()) {
            channelFuture = null;
            serverBootstrap = null;
            handlerGroup = null;
            bossGroup = null;
            workerGroup = null;
        }
    }
}
